﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Autofac;
using Autofac.Builder;
using Autofac.Core;

namespace PlutoStudio.Dependency
{
    internal sealed class IocManager : IIocManager
    {
        private static IocManager _instance;
        private IContainer _container;

        public IContainer IocContainer => _container;

        /// <summary>
        /// The Singleton instance.
        /// </summary>
        public static IocManager Instance => _instance;

        /// <summary>
        /// List of all registered conventional registrars.
        /// </summary>
        private readonly List<IConventionalDependencyRegistrar> _conventionalRegistrars;


        static IocManager()
        {

            _instance = new IocManager();
        }

        IocManager()
        {
            _conventionalRegistrars = new List<IConventionalDependencyRegistrar>();
            Initialization();
        }

        void Initialization()
        {
            var builder = new ContainerBuilder();
            builder.RegisterInstance(this).AsSelf().AsImplementedInterfaces();
            _container = builder.Build();
        }

        public bool IsRegistered(Type type)
        {
            return IocContainer.IsRegistered(type);
        }

        public bool IsRegistered<TType>()
        {
            return IocContainer.IsRegistered<TType>();
        }

        public void Register<T>(LifetimeScopeStyle lifeStyle = LifetimeScopeStyle.Singleton) where T : class
        {
            var builder = new ContainerBuilder();
            
            builder.RegisterType<T>().AsSelf().AsImplementedInterfaces().AsLifeStyle(lifeStyle).PropertiesAutowired(new PropertySelector());
#pragma warning disable CS0618 // Type or member is obsolete
            builder.Update(IocContainer);
#pragma warning restore CS0618 // Type or member is obsolete
        }

        public void Register(Type type, LifetimeScopeStyle lifeStyle = LifetimeScopeStyle.Singleton)
        {
            var builder = new ContainerBuilder();
            builder.RegisterType(type).AsSelf().AsImplementedInterfaces().AsLifeStyle(lifeStyle).PropertiesAutowired(new PropertySelector());
#pragma warning disable CS0618 // Type or member is obsolete
            builder.Update(IocContainer);
#pragma warning restore CS0618 // Type or member is obsolete
        }

        public void Register(Type type, Type impl, LifetimeScopeStyle lifeStyle = LifetimeScopeStyle.Singleton)
        {
            var builder = new ContainerBuilder();
            builder.RegisterType(impl).As(type).AsLifeStyle(lifeStyle).PropertiesAutowired(new PropertySelector());
#pragma warning disable CS0618 // Type or member is obsolete
            builder.Update(IocContainer);
#pragma warning restore CS0618 // Type or member is obsolete
        }

        public void RegisterGeneric(Type type, Type impl, LifetimeScopeStyle lifeStyle = LifetimeScopeStyle.Singleton)
        {
            var builder = new ContainerBuilder();
            builder.RegisterGeneric(impl).As(type).AsLifeStyle(lifeStyle).PropertiesAutowired(new PropertySelector());
#pragma warning disable CS0618 // Type or member is obsolete
            builder.Update(IocContainer);
#pragma warning restore CS0618 // Type or member is obsolete
        }

        /// <summary>
        /// Adds a dependency registrar for conventional registration.
        /// </summary>
        /// <param name="registrar">dependency registrar</param>
        public void AddConventionalRegistrar(IConventionalDependencyRegistrar registrar)
        {
            _conventionalRegistrars.Add(registrar);
        }

        public void RegisterAssemblyByConvention(Assembly assembly)
        {
            RegisterAssemblyByConvention(assembly, new ConventionalRegistrationConfig());
        }

        public void RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config)
        {
            var builder = new ContainerBuilder();
            var context = new ConventionalRegistrationContext(assembly, builder, config);
            foreach (var item in _conventionalRegistrars)
            {
                item.RegisterAssembly(context);
            }
            if (config.InstallInstallers)
            {
                builder.RegisterAssemblyModules(assembly);
            }
#pragma warning disable CS0618 // Type or member is obsolete
            builder.Update(IocContainer);
#pragma warning restore CS0618 // Type or member is obsolete

        }
        public T Resolve<T>()
        {
            return IocContainer.Resolve<T>();
        }
        public T Resolve<T>(object argumentsAsAnonymousType)
        {
            return IocContainer.Resolve<T>(argumentsAsAnonymousType.ToParameters());
        }

        public T Resolve<T>(Type type)
        {
            return (T)IocContainer.Resolve(type);
        }
        public T Resolve<T>(Type type,object argumentsAsAnonymousType)
        {
            return (T)IocContainer.Resolve(type,argumentsAsAnonymousType.ToParameters());
        }

        public object Resolve(Type type)
        {
            return IocContainer.Resolve(type);
        }

        public object Resolve(Type type,object argumentsAsAnonymousType)
        {
            return IocContainer.Resolve(type,argumentsAsAnonymousType.ToParameters());
        }
        public T[] ResolveAll<T>()
        {
            return IocContainer.Resolve<IEnumerable<T>>().ToArray();
        }


        #region IDisposable Support
        bool disposedValue; // To detect redundant calls

        void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: dispose managed state (managed objects).
                    IocContainer.Dispose();
                }

                // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
                // TODO: set large fields to null.

                disposedValue = true;
            }
        }

        // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
        // ~IocManager() {
        //   // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        //   Dispose(false);
        // }

        // This code added to correctly implement the disposable pattern.
        public void Dispose()
        {
            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
            Dispose(true);
            // TODO: uncomment the following line if the finalizer is overridden above.
            // GC.SuppressFinalize(this);
        }

        public void Register<TType, TImpl>(TImpl component,bool injectProperties=true) where TType : class where TImpl : class, TType
        {
            var builder = new ContainerBuilder();
            builder.RegisterInstance(component).AsSelf().As<TType>().SingleInstance().If(injectProperties, b=>b.PropertiesAutowired(new PropertySelector()));
#pragma warning disable CS0618 // 类型或成员已过时
            builder.Update(IocContainer);
#pragma warning restore CS0618 // 类型或成员已过时
        }

        public void Register<TType, TImpl>(LifetimeScopeStyle lifeStyle) where TType : class where TImpl : class, TType
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<TImpl>().AsSelf().As<TType>().AsLifeStyle(lifeStyle).PropertiesAutowired(new PropertySelector());
#pragma warning disable CS0618 // 类型或成员已过时
            builder.Update(IocContainer);
#pragma warning restore CS0618 // 类型或成员已过时
        }

        public void Register<TComponent>(Type type, TComponent component, bool injectProperties = true) where TComponent : class
        {
            var builder = new ContainerBuilder();
            builder.RegisterInstance(component).AsSelf().As(type).SingleInstance().If(injectProperties,b=>b.PropertiesAutowired(new PropertySelector()));
#pragma warning disable CS0618 // 类型或成员已过时
            builder.Update(IocContainer);
#pragma warning restore CS0618 // 类型或成员已过时
        }

        public void Register<TComponent>(TComponent component, bool injectProperties = true) where TComponent : class
        {
            var builder = new ContainerBuilder();
            builder.RegisterInstance(component).AsSelf().AsImplementedInterfaces().SingleInstance().If(injectProperties,b=>b.PropertiesAutowired(new PropertySelector()));
#pragma warning disable CS0618 // 类型或成员已过时
            builder.Update(IocContainer);
#pragma warning restore CS0618 // 类型或成员已过时
        }

        public void RegisterModule(IModule module)
        {
            var builder = new ContainerBuilder();
            builder.RegisterModule(module);
#pragma warning disable CS0618 // 类型或成员已过时
            builder.Update(IocContainer);
#pragma warning restore CS0618 // 类型或成员已过时

        }

        public void Install(IIocInstaller installer)
        {
            var builder = new ContainerBuilder();
            builder.RegisterModule(installer);
#pragma warning disable CS0618 // 类型或成员已过时
            builder.Update(IocContainer);
#pragma warning restore CS0618 // 类型或成员已过时
        }
        #endregion

    }

    internal static class AutofacExtensions
    {
        public static IRegistrationBuilder<TLimit, TActivatorData, TStyle> AsLifeStyle<TLimit, TActivatorData, TStyle>(this IRegistrationBuilder<TLimit, TActivatorData, TStyle> registration, LifetimeScopeStyle lifetimeStyle)
        {
            switch (lifetimeStyle)
            {
                case LifetimeScopeStyle.Singleton:
                    registration.SingleInstance();
                    break;
                case LifetimeScopeStyle.Transient:
                    registration.InstancePerDependency();
                    break;
                case LifetimeScopeStyle.InstancePerLifetimeScope:
                    registration.InstancePerLifetimeScope();
                    break;
                default:
                    registration.InstancePerDependency();
                    break;
            }
            return registration;
        }

        public static ILifetimeScope BeginLifetimeScope(this IIocManager iocManager)
        {
            return iocManager.IocContainer.BeginLifetimeScope();
        }
    }
}
